package edu.ustb.efws.pipeline.service;

import edu.ustb.efws.pipeline.domain.TableOpt;
import edu.ustb.efws.pipeline.mapper.TableOptMapper;
import edu.ustb.efws.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * 日志表操作
 *
 * @author TS Group
 */
@Service
public class TableOptService {
    private static final Logger LOGGER = LoggerFactory.getLogger(TableOptService.class);

    private final TableOptMapper mapper;

    /**
     * 创建日志表
     *
     * @param mapper {@link TableOptMapper}
     */
    @Autowired
    public TableOptService(TableOptMapper mapper) {
        this.mapper = mapper;
    }

    /**
     * 表不存在创建表
     *
     * @param tableName 表名
     * @param createTableOpt 创建表方法
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void createIfAbsent(String tableName, Consumer<String> createTableOpt){
        int count = mapper.count(tableName);
        if(count == 0){
            try{
                createTableOpt.accept(tableName);
                saveCreateResult(tableName, 0, buildPartition(0));
            }catch (Exception e){
                LOGGER.error("Create table fail tableName={}, error={}", tableName, e.getMessage());
            }
        }
    }

    /**
     * 创建分区名称
     *
     * @param month 月份
     * @return 分区名称
     */
    private String buildPartition(int month){
        return String.format("p%d", month);
    }

    /**
     * 保存创建结果
     *
     * @param tableName 表名
     * @param month     月份
     * @param partition 分区名称
     */
    private void saveCreateResult(String tableName, int month, String partition){
        TableOpt t = new TableOpt();
        t.setTableName(tableName);
        t.setMonth(month);
        t.setPartition(partition);
        t.setDrop(false);
        t.setRemark("");
        mapper.insert(t);
    }

    /**
     * 创建分区
     *
     * @param tableName 表名
     * @param tableMinMonth 需要创建表的最小月份
     * @param createPartitionOpt 创建分区操作
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void createPartition(String tableName, int tableMinMonth, BiConsumer<Integer, String> createPartitionOpt){
        LocalDate toMonth = LocalDate.now().plusMonths(1);
        LocalDate fromMonth = getFromMonth(tableName, tableMinMonth);
        if(toMonth.compareTo(fromMonth) <= 0){
            return ;
        }

        for(int i = 0; i < 50; i++){
            LocalDate localDate = fromMonth.plusMonths(i);
            createPartition(createPartitionOpt, tableName, DateUtils.localDateToMonthNum(localDate));
            if(localDate.compareTo(toMonth) >= 0){
                break;
            }
        }
    }

    /**
     * 获取开始生产表月份
     *
     * @param tableName 表名
     * @param tableMinMonth 需要创建表的最小月份
     * @return 生成表开始时间
     */
    private LocalDate getFromMonth(String tableName, int tableMinMonth){
        Integer month = mapper.maxMonth(tableName);
        if(month != null){
            return DateUtils.monthNumToLocalData(month).plusMonths(1);
        }

        if(tableMinMonth == -1){
            return LocalDate.now();
        }else{
            return DateUtils.monthNumToLocalData(tableMinMonth);
        }
    }

    /**
     * 创建分区
     *
     * @param createPartitionOpt 创建分区
     * @param tableName 表名
     * @param month 月份
     */
    private void createPartition(BiConsumer<Integer, String> createPartitionOpt, String tableName, int month){
        try{
            String partition = buildPartition(month);
            createPartitionOpt.accept(month, partition);
            saveCreateResult(tableName, month, partition);
        }catch (Exception e){
            LOGGER.error("Create table partition fail tableName={}, month={}, error={}", tableName, month, e.getMessage());
        }
    }

    /**
     * 删除分区
     *
     * @param tableName 表名
     * @param fromMonth 开始月份
     * @param toMonth 结束月份
     * @param remark 备注
     * @param dropPartitionOpt 删除分区操作
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void dropPartition(String tableName, int fromMonth, int toMonth, String remark,
                              Consumer<String> dropPartitionOpt){

        List<TableOpt> tableOpts = mapper.find(tableName, fromMonth, toMonth);
        for(TableOpt opt: tableOpts){
            if(opt.getDrop()){
                continue;
            }

            try{
                dropPartitionOpt.accept(opt.getPartition());
                mapper.updateDrop(tableName, opt.getMonth(), remark);
            }catch (Exception e){
                LOGGER.error("Drop table partition fail tableName={}, month={}, partition={}, error={}",
                        tableName, opt.getMonth(), opt.getPartition(), e.getMessage());
            }
        }
    }

    /**
     * 是否表记录
     *
     * @return true 有表记录
     */
    public boolean hasTable(){
        return mapper.countAll() > 0;
    }
}
